﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.Diagnostics;
using LogLib;

namespace IPCPages
{
    public class Pipeline : IDisposable
    {
        public Guid ID { get; private set; }

        private NamedPipeServerStream Server;
        private Task Task;
        private AutoResetEvent Get, Got;
        private string inputContext;
        private StreamWriter Writer;
        private StreamReader Reader;

        private Thread readThread = null;

        public const int MaxServer = 100;
        public const string ServerName = "MainStartup";
        public const int ServerWaitReadMillisecs = 2000; //10s
        public const int MaxTimeout = 3;
        private CancellationTokenSource tokenSource = new CancellationTokenSource();
        public bool IsDisposed
        {
            private set;
            get;
        } = false;

        public Pipeline()
        {
            ID = Guid.NewGuid();
            Get = new AutoResetEvent(false);
            Got = new AutoResetEvent(false);
            Server = new NamedPipeServerStream(ServerName, PipeDirection.InOut, MaxServer);
        }

        public void Start()
        {
            Task = Task.Factory.StartNew(TaskRun);
        }

        public void Stop()
        {
            tokenSource.Cancel();
            if (readThread != null && readThread.ThreadState == System.Threading.ThreadState.Running)
            {
                readThread.Abort();
            }
        }

        private async void TaskRun()
        {
            await Server.WaitForConnectionAsync(tokenSource.Token);
            if (tokenSource.IsCancellationRequested)
            {
                return;
            }
            PipelinePool.CreatePipeLineAsync();
            try
            {
                Writer = new StreamWriter(Server);
                Reader = new StreamReader(Server);
                while (true)
                {
                    if (tokenSource.IsCancellationRequested)
                    {
                        return;
                    }
                    var input = TryReadLine();
                    if (string.IsNullOrEmpty(input))
                        break;
                    string msg = string.Format("Server {0} Get Message:{1}", ID, input);
                    Log.WriteAsync(msg);
                    string[] array = input.Split(new char[] { '#' });
                    if (array.Length == 3)
                    {
                        int code = int.Parse(array[1]);
                        PipelinePool.OnMsgReceived(array[0], code, array[2]);
                        break;
                    }
                }
                Log.WriteAsync(string.Format("管道{0}即将关闭", ID));
            }
            catch (TimeoutException)
            {
                Log.WriteAsync(string.Format("管道{0}超时次数过多，视为丢失链接", ID));
            }
            catch (Exception ex)
            {
                Log.WriteException(ex.ToString());
            }
            finally
            {
                Dispose();
            }
        }

        private void OnReading()
        {
            try
            {
                Get.WaitOne(1000);
                if (tokenSource.IsCancellationRequested)
                {
                    return;
                }
                inputContext = Reader.ReadLine();
            }
            catch
            {
                inputContext = "";
            }
            finally
            {
                Got.Set();
            }
        }

        private string TryReadLine()
        {

            int TimeOutCount = 0;
            readThread = new Thread(OnReading);
            readThread.Start();
            Get.Set();
            if (tokenSource.IsCancellationRequested)
            {
                return "";
            }
            while (!Got.WaitOne(ServerWaitReadMillisecs))
            {
                if (tokenSource.IsCancellationRequested)
                {
                    return "";
                }
                if (TimeOutCount++ > MaxTimeout)
                {
                    readThread.Abort();
                    throw new TimeoutException();
                }
                Log.WriteAsync(string.Format("管道{0}第{1}次超时", ID, TimeOutCount));
            }
            return inputContext;
        }

        public void Dispose()
        {
            if (Server.IsConnected)
            {
                Server.Close();
                Server.Dispose();
            }
            Get.Dispose();
            Got.Dispose();
            PipelinePool.DisposablePipeLineAsync(ID);
        }
    }
}
